home *** CD-ROM | disk | FTP | other *** search
/ Business Shareware / Business Shareware.iso / start / disk / copyd11 / wsscopyd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-06  |  38.0 KB  |  1,379 lines

  1. #include <stdio.h>
  2. #include <bios.h>
  3. #include <conio.h>
  4. #include <dir.h>
  5. #include <dos.h>
  6. #include <io.h>
  7. #include <mem.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <alloc.h>
  12.  
  13. #ifdef toupper
  14.     #undef toupper        /* don't want a macro version with side effects */
  15. #endif
  16.  
  17. /******************************  Warning !!!! ******************************
  18.  
  19.     This program uses writes to absolute sectors for copying and can format
  20.     disk tracks.  If you make changes, be sure that you know what you are
  21.     doing.  In particular, note that if a secondary floppy controller is in
  22.     use, the drive numbers used by int 13h for drives other than A: and B:
  23.     depend on the device driver used to access that controller.  On my system,
  24.     the two floppies on my secondary controller are H: and I:, yet they respond
  25.     as 4 and 5 to INT 13h.
  26.  
  27. ****************************************************************************/
  28.  
  29. /* List of possible enhancements
  30.     1. Insert a proper volume serial number (different from the original).
  31.         Ignore this difference when verifying.
  32.     2. Allow output drive != input.
  33.     3. Configure colors.
  34.     4. Use extended or expanded memory rather than a disk file.
  35.  */
  36.  
  37. #define LOGICAL        int
  38. #define FALSE            0
  39. #define TRUE            1
  40. #define START_CLOCK    0
  41. #define READ_CLOCK    1
  42. #define ERR                -1
  43. #define NO_ERR            0
  44.  
  45. #define BACKGROUND_COLOR    BLUE
  46. #define TEXT_COLOR            WHITE
  47. #define READ_INDICATOR        RED
  48. #define FORMAT_INDICATOR    MAGENTA
  49. #define WRITE_INDICATOR        GREEN
  50. #define VERIFY_INDICATOR    YELLOW
  51.  
  52. #define BYTES_PER_SECTOR    512
  53.  
  54. #define MAX_SECTORS    60
  55. #define BUFFER_SIZE    MAX_SECTORS*512
  56.  
  57. #define ERROR_ALERT()    {sound(45); delay(500);    nosound();}
  58. #define UP_N(n)    {gotoxy(1, wherey() - n);}
  59. #define UP_ONE_AND_CLEAR()    \
  60.     {cprintf("\r"); clreol(); gotoxy(1, wherey() - 1);    clreol();}
  61.  
  62. /* biosdisk() BIOS function cmd calls */
  63.  
  64. #define RESET_CONTROLLER    0
  65. #define READ_TRK                2
  66. #define WRITE_TRK                3
  67. #define VERIFY_TRK            4
  68. #define FORMAT_TRK            5
  69.  
  70. #define FLOPPY_360K 0
  71. #define FLOPPY_12MB 1
  72. #define FLOPPY_720K 2
  73. #define FLOPPY_144M 7
  74.  
  75. /* Prototypes for local routines
  76.  */
  77. char *getvolid(int dos_drive);
  78. void far hardset(void);
  79. void measure_speed(int function, char *thing_timed);
  80. void statline(int sectors, int color);
  81. int disk_format(int int13_drive, int drive_type);
  82. void erase_temp_file(void);
  83. LOGICAL disk_rw(int int13_drive, int action, int first_sector, int nsects,
  84.                      char *buffer);
  85. void reset_disk_controller(void);
  86. void disk_error_print(unsigned int error_code);
  87.  
  88. /* wsscopyd - copy a disk using another disk for temporary storage of
  89.     disk image.
  90.     usage: WSScopyd [options] d[:] [options]
  91.  
  92.     Options (case ignored, / or - switchchar allowed):
  93.         -B - beep when disk change is needed
  94.         -Cn - number of copies
  95.         -F - format target disk
  96.         -R fn - reuse saved file fn
  97.         -S fn - save disk file with name fn
  98.         -T - display time used
  99.         -V - verify by reading floppy and comparing to original data
  100.         -X1 - disk is single-sided, 10 sectors per track, 80 tracks
  101.         -X2 - disk is double-sided, 10 sectors per track, 80 tracks
  102.         -X3 - disk is RX-50 (like X1 but tracks after 1st two are 2:1 interleave)
  103.                 -X format source disks do not need to have a valid boot sector.
  104.                 Volume label will not be displayed during copying when -X is
  105.                 specified.
  106.  
  107.     Compile with compact or large model else allocation of buffers will fail.
  108.  
  109.     DESQView compatibility: DOS Buffers for EMS should be set larger than the
  110.     default value of 2K else run time will be excessive.  4K is the minimum
  111.     acceptable value, but 8K is better.  Performance continues to improve
  112.     up to at least 32K.  The -t option can be used to time operation with
  113.     different values.  The following read times were obtained on a 386/25:
  114.  
  115.      EMS buffer size    Seconds
  116.              2K            307.8      (default setting)
  117.              4K            161.6
  118.              8K             93.5
  119.             16K             76.3
  120.             32K             68.4
  121.         under DOS          59.4
  122.  
  123.     All screen output leaves a 2-character margin on left and right so that
  124.     everything will be visible even if there is a window border at the edge
  125.     of the screen.  The right margin also eliminates any worry about whether
  126.     writing in the last character position of the bottom row causes the screen
  127.     to scroll.
  128.  */
  129.  
  130. volatile int Hard_error;    /* Set to TRUE by critical error handler.  Note
  131.                                         that any call to absread or abswrite can
  132.                                         potentially set Hard_error, even if the
  133.                                         call succeeds after retrying. */
  134. int Fail_ignore = 3;            /* tell critical error handler to fail on error */
  135. int Tr_per_dsk = 40;
  136. int Sd_per_dsk = 1;
  137. int Sectors_per_track = 8;
  138. int Sectors_per_disk;
  139. LOGICAL Is_X;
  140. int X_type;
  141. void interrupt (*System_dpb)();
  142.  
  143. struct DPB
  144.     {
  145.     char spec1;            /* first specify byte */
  146.     char spec2;        /* second specify byte */
  147.     char stop_delay;    /* delay until motor turned off (ticks) */
  148.     char bps;            /* bytes per sector 0=128, 1=256, 2=512, 3=1024 */
  149.     char spt;            /* sectors per track */
  150.     char gap;            /* gap between sectors (2Ah for 5.25", 1Bh for 3.5") */
  151.     char data_l;        /* data length (ignored if bps is non-zero) */
  152.     char gap_l;            /* formatting gap length (50h for 5.25", 6ch for 3.5") */
  153.     char filler;        /* format filler byte (default F6h) */
  154.     char settle;        /* head settle time in milliseconds */
  155.     char start_time;    /* motor start time in 1/8 seconds */
  156.     } my_dpb;
  157.  
  158. char Tempfile[100];        /* file name for storage disk to be copied, global
  159.                                     so that exit routine can erase it if needed */
  160.  
  161. void main(int argc, char **argv)
  162.     {
  163.     char    huge *buffer,                /* buffer for data from floppy or save file */
  164.             huge *buffer2,            /* buffer for data from floppy when verifying */
  165.             *cfg_path,            /* full path to configuration file */
  166.             *cmp1,                /* pointer looping through buffer */
  167.             *cmp2,                /* pointer looping through buffer2 */
  168.             *openmode,            /* mode for opening disk file, read or update */
  169.             *temp_path,            /* TEMP or TMP environment variable */
  170.             *volname;            /* volume name or <none> */
  171.     FILE    *fp;                    /* file pointer for save file */
  172.     int   disk_cap,            /* disk drive capacity from cfg file, 1=360k, etc */
  173.             dos_drive,            /* drive number for DOS calls, A=0, B=1,... */
  174.          drive_type,            /* current drive type */
  175.             fat_sub,                /* 0 or 1 index into FAT mask and test arrays */
  176.             first_sec,            /* first sector to be read from floppy */
  177.             i,                        /* for loop index */
  178.             icopy,                /* counter for number of copies made */
  179.             int13_drive,        /* drive number for int 13h calls */
  180.             key,                    /* key hit in response to a prompt */
  181.             max_sectors_use,    /* number of sectors used in read/write buffer,
  182.                                         a multiple of the number of sectors per track */ 
  183.             n_alloc,                /* number of allocation units addressed by FAT */
  184.             ncopies,                /* number of copies requested */
  185.             ndrives,                /* number of drives defined in config file */
  186.             nread,                /* number of sectors to read in one call */
  187.             nver,                    /* number of sectors to verify in one call */
  188.             nwrite,                /* number of sectors to write in one call */
  189.             status,                /* status from ioctl check for removability or
  190.                                         from disk_rw call */
  191.             tot_sects;            /* number of sectors on source disk */
  192.     unsigned int    j,            /* for loop index */
  193.                         nbytes;    /* number of bytes to verify on one disk read */
  194.     LOGICAL  format,            /* if TRUE, format disks */
  195.                 have_drive,        /* set TRUE if command line specified drive */
  196.                 save_temp_file,/* if TRUE, temp file is saved */
  197.                 time,                /* if TRUE, display time used by each operation */
  198.                 use_beep,        /* if TRUE, sound alarm when disk swap is needed */
  199.                 use_old_file,    /* if TRUE, use an old temp file */
  200.                 verify;            /* if TRUE, verify copy by re-reading and comparing
  201.                                         with what should have been written */
  202.     char beep;                    /* control-g if alarm enabled, blank if not */
  203.     char    cfg_load_path[MAXPATH],        /* program load path with wsscopyd.cfg
  204.                                                     appended */
  205.             disk_letter[80],            /* scanf buffer for disk letter read */
  206.             disk_type[80],                /* scanf buffer for disk type read */
  207.             int13_number[80],            /* scanf buffer for int 13h disk number */
  208.             load_dir[MAXDIR],            /* directory program was loaded from */
  209.             load_drive[MAXDRIVE];    /* drive program was loaded from */
  210.  
  211.     unsigned int tseg;
  212.  
  213.     struct BPB                    /* info in floppy boot sector */
  214.         {
  215.         char jump[3];
  216.         char oem[8];
  217.         short bytes_per_sector;
  218.         char sectors_per_alloc;
  219.         short reserved_secs;
  220.         char num_fats;
  221.         short num_root_dir;
  222.         short total_sectors;
  223.         char media_desc;
  224.         short sectors_per_fat;
  225.         short sectors_per_track;
  226.         short num_heads;
  227.         short num_hidden;
  228.         } huge *bpb;
  229.  
  230.     struct
  231.         {
  232.         int number;
  233.         int type;
  234.         char letter;
  235.         } disk_table[26];
  236.  
  237.     short *fat_12;                        /* used for indexing through 12-bit FAT */
  238.     int fat12_mask[2] = {0xfff0, 0xfff};        /* masks to select one FAT entry */
  239.     int fat12_test[2] = {0xff70, 0xff7};        /* bad block FAT values */
  240.  
  241.     directvideo = 0;        /* use ROM BIOS, should avoid bleedthrough in DV
  242.                                     windows on pre-386 CPU's */
  243.  
  244.     textbackground(BACKGROUND_COLOR);
  245.     textcolor(TEXT_COLOR);
  246.     cprintf("\r\n  WSScopyd version 1.1\r\n"
  247.               "  Copyright December 6, 1992 "
  248.               "by Robert W. Babcock and WSS Division of DDC\r\n"
  249.               "  Unlimited non-commercial use authorized\r\n\n\n");
  250.  
  251. /* Need DOS 3+ for removability check
  252.  */
  253.     if(_osmajor < 3)
  254.         {
  255.         cprintf("  Sorry, WSScopyd requires DOS 3 or higher\r\n");
  256.         exit(1);
  257.         }
  258.  
  259. /* Read configuration file.  First look in directory program was loaded from,
  260.     then try current directory and path.
  261.  */
  262.     fnsplit(argv[0], load_drive, load_dir, NULL, NULL);
  263.     fnmerge(cfg_load_path, load_drive, load_dir, "WSSCOPYD", "CFG");
  264.     if(0 == access(cfg_load_path, 0))
  265.         cfg_path = cfg_load_path;
  266.     else if(NULL == (cfg_path = searchpath("WSSCOPYD.CFG")))
  267.         {
  268.         cprintf("  Configuration file WSSCOPYD.CFG must be in current directory "
  269.                   "or on path\r\n"
  270.                   "  See documentation file for format of this file\r\n");
  271.         exit(1);
  272.         }
  273.     if(NULL == (fp = fopen(cfg_path, "r")))
  274.         {
  275.         cprintf(" %s cannot be opened\r\r", cfg_path);
  276.         exit(1);
  277.         }
  278. /* Skip over comments at beginning of configuration file
  279.  */
  280.     for(;;)
  281.         {
  282.         if(NULL == fgets(Tempfile, 99, fp))
  283.             break;
  284.         if(0 == strncmp(Tempfile, "----------", 10))
  285.             break;
  286.         }
  287.     for(ndrives=0; ndrives<26 ; ndrives++)
  288.         {
  289.         if(3 != fscanf(fp, "%s %s %s", &disk_letter, &int13_number, &disk_type))
  290.             break;
  291.         disk_table[ndrives].letter = *disk_letter;
  292.         disk_table[ndrives].number = atoi(int13_number);
  293.         if(0 == strncmp(disk_type, "360", 3))
  294.             disk_cap = FLOPPY_360K;
  295.         else if(0 == strncmp(disk_type, "12", 2))
  296.             disk_cap = FLOPPY_12MB;
  297.         else if(0 == strncmp(disk_type, "1.2", 3))
  298.             disk_cap = FLOPPY_12MB;
  299.         else if(0 == strncmp(disk_type, "72", 2))
  300.             disk_cap = FLOPPY_720K;
  301.         else if(0 == strncmp(disk_type, "144", 3))
  302.             disk_cap = FLOPPY_144M;
  303.         else if(0 == strncmp(disk_type, "1.44", 4))
  304.             disk_cap = FLOPPY_144M;
  305.         else
  306.             {
  307.             cprintf("  Configuration file error\r\n"
  308.                       "  Floppy drive %c, number %d, type %s not recognized\r\n",
  309.                       disk_table[ndrives].letter, disk_table[ndrives].number,
  310.                       disk_type);
  311.             exit(1);
  312.             }
  313.         disk_table[ndrives].type = disk_cap;
  314.         }
  315.     fclose(fp);
  316.  
  317.     if(0 == ndrives)
  318.         {
  319.         cprintf("  No drive info readable in %s\r\n", cfg_path);
  320.         exit(1);
  321.         }
  322.  
  323.     if(argc < 2)
  324.         {
  325.         cprintf("  Usage: WSScopyd [options] drive: [options]\r\n"
  326.                   "    Options are marked by / or -\r\n"
  327.                   "      -B - beep when disk change is needed\r\n"
  328.                   "      -Cn - number of copies\r\n"
  329.                   "      -F - format target disk\r\n"
  330.                   "      -R fn - reuse saved file fn\r\n"
  331.                   "      -S fn - save disk file with name fn\r\n"
  332.                   "      -T - display time used\r\n"
  333.                   "      -V - verify by reading floppy and comparing to original data\r\n"
  334.                   "      -X1 - disk is single-sided, 10 sectors per track, 80 tracks\r\n"
  335.                   "      -X2 - disk is double-sided, 10 sectors per track, 80 tracks\r\n"
  336.                   "      -X3 - disk is RX-50\r\n");
  337.         exit(1);
  338.         }
  339.  
  340. /* Alignment of bpb, buffer and (later) buffer2 are adjusted so that they
  341.     do not cross a 64K boundary.  This avoids problems with int 13h in some
  342.     environments, particularly under OS/2.  I think that int 13h never likes
  343.     buffers crossing 64K, but the BIOS usually fixes things up for you.
  344.  */
  345.     bpb = (struct BPB *)farmalloc(1024);
  346.     tseg = FP_SEG(bpb)+FP_OFF(bpb)/16;
  347.     if((0xf000 & (tseg+512/16)) != (0xf000 & tseg))
  348.         bpb += 512;
  349.     if(NULL == (buffer = farmalloc(2L*BUFFER_SIZE)))
  350.         {
  351.         cprintf("  Error: out of memory\r\n");
  352.         exit(1);
  353.         }
  354.     tseg = FP_SEG(buffer)+FP_OFF(buffer)/16;
  355.     if((0xf000 & (tseg+BUFFER_SIZE/16)) != (0xf000 & tseg))
  356.         buffer += BUFFER_SIZE;
  357.  
  358. /* Parse command line
  359.  */
  360.     use_beep = format = time = verify = Is_X = FALSE;
  361.     have_drive = save_temp_file = use_old_file = FALSE;
  362.     ncopies = 1;
  363.     for(argc--, argv++ ; argc>0; argc--, argv++)
  364.         {
  365.         if('-' == **argv || '/' == **argv)
  366.             {
  367.             (*argv)++;
  368.             switch(toupper(**argv))
  369.                 {
  370.                 case 'B':
  371.                     use_beep = TRUE;
  372.                     continue;
  373.  
  374.                 case 'C':
  375.                     (*argv)++;
  376.                     if('\0' == **argv)
  377.                         {
  378.                         argv++;
  379.                         argc--;
  380.                         if(0 == argc)
  381.                             {
  382.                             cprintf("  Error, number of copies missing\r\n");
  383.                             exit(1);
  384.                             }
  385.                         }
  386.                     ncopies = atoi(*argv);
  387.                     continue;
  388.  
  389.                 case 'F':
  390.                     format = TRUE;
  391.                     continue;
  392.  
  393.                 case 'R':
  394.                     use_old_file = TRUE;
  395.                     (*argv)++;
  396.                     if('\0' == **argv)
  397.                         {
  398.                         argv++;
  399.                         argc--;
  400.                         if(0 == argc)
  401.                             {
  402.                             cprintf("  Error, old save file name missing\r\n");
  403.                             exit(1);
  404.                             }
  405.                         }
  406.                     strcpy(Tempfile, *argv);
  407.                     continue;
  408.  
  409.                 case 'S':
  410.                     save_temp_file = TRUE;
  411.                     (*argv)++;
  412.                     if('\0' == **argv)
  413.                         {
  414.                         argv++;
  415.                         argc--;
  416.                         if(0 == argc)
  417.                             {
  418.                             cprintf("  Error, save file name missing\r\n");
  419.                             exit(1);
  420.                             }
  421.                         }
  422.                     strcpy(Tempfile, *argv);
  423.                     continue;
  424.  
  425.                 case 'T':
  426.                     time = TRUE;
  427.                     continue;
  428.  
  429.                 case 'V':
  430.                     verify = TRUE;
  431.                     continue;
  432.  
  433.                 case 'X':
  434.                     Is_X = TRUE;
  435.                     (*argv)++;
  436.                     if('\0' == **argv)
  437.                         {
  438.                         argv++;
  439.                         argc--;
  440.                         if(0 == argc)
  441.                             {
  442.                             cprintf("  Error, -X should be followed by 1,2 or 3\r\n");
  443.                             exit(1);
  444.                             }
  445.                         }
  446.                     X_type = atoi(*argv);
  447.                     if(X_type < 1 || X_type > 3)
  448.                         {
  449.                         cprintf("  Unrecognized Xn option\r\n");
  450.                         exit(1);
  451.                         }
  452.                     continue;
  453.  
  454.                 default:
  455.                     cprintf("  Unrecognized option %s, "
  456.                                 "run with no arguments for help\r\n", *argv);
  457.                     exit(1);
  458.                 }
  459.             }
  460.         else
  461.             {
  462.             if(have_drive)
  463.                 {
  464.                 cprintf("  Error, missing switchchar or drive specified twice\r\n");
  465.                 exit(1);
  466.                 }
  467.             else
  468.                 {
  469.                 dos_drive = toupper(**argv);
  470.                 have_drive = TRUE;
  471.                 }
  472.             }
  473.         }
  474.  
  475. /* Check for valid options requested
  476.  */
  477.     if(!have_drive)
  478.         {
  479.         cprintf("  Error, drive not specified\r\n");
  480.         exit(1);
  481.         }
  482.  
  483.     if(ncopies < 0 || ((0 == ncopies) && !save_temp_file))
  484.         {
  485.         cprintf("  Error, requested number of copies is %d\r\n", ncopies);
  486.         exit(1);
  487.         }
  488.  
  489.     for(i=0; i<ndrives; i++)
  490.         {
  491.         if(dos_drive == disk_table[i].letter)
  492.             {
  493.             int13_drive = disk_table[i].number;
  494.             drive_type = disk_table[i].type;
  495.             goto found_drive_info;
  496.             }
  497.         }
  498.     cprintf("  Drive %c does not appear in WSSCOPYD.CFG\r\n", dos_drive);
  499.     exit(1);
  500.  
  501. /* Kill the high bit of format_drive to avoid the possibility of it pointing
  502.     at a hard disk due to an error in specifying drive.
  503.  */
  504. found_drive_info:
  505.     int13_drive &= 0x7f;
  506.     dos_drive -= 'A';
  507.  
  508.     if(save_temp_file && use_old_file)
  509.         {
  510.         cprintf("  Error, -R and -S options are mutually exclusive\r\n");
  511.         exit(1);
  512.         }
  513.  
  514.     beep = use_beep ? '\a' : ' ';
  515.  
  516.     if(verify)
  517.         {
  518.         if(NULL == (buffer2 = farmalloc(2L*BUFFER_SIZE)))
  519.             {
  520.             cprintf("  Error: not enough memory for verify buffer\r\n");
  521.             exit(1);
  522.             }
  523.         tseg = FP_SEG(buffer2)+FP_OFF(buffer2)/16;
  524.         if((0xf000 & (tseg+BUFFER_SIZE/16)) != (0xf000 & tseg))
  525.             buffer2 += BUFFER_SIZE;
  526.         }
  527.  
  528. /* Make sure the disk to be copied is removable
  529.  */
  530.     status = ioctl(dos_drive+1, 8, 0, 0);
  531.     if(0 != status)
  532.         {
  533.         cprintf("  Error: WSScopyd only works for removable media\r\n");
  534.         exit(1);
  535.         }
  536.  
  537.     if(!(save_temp_file || use_old_file))
  538.         {
  539.         temp_path = getenv("TEMP");
  540.         if(NULL == temp_path)
  541.             temp_path = getenv("TMP");
  542.         if(NULL == temp_path)
  543.             {
  544.             cprintf("  Error: environment variable TEMP or TMP must be defined\r\n");
  545.             exit(1);
  546.             }
  547.         strcpy(Tempfile, temp_path);
  548.         if('\\' != Tempfile[strlen(Tempfile)-1])
  549.             strcat(Tempfile, "\\");
  550.         strcat(Tempfile, "copyd.$$$");
  551.         }
  552.  
  553. /* Set up critical error handler.  DOS undoes this automatically when we exit.
  554.  */
  555.     hardset();
  556.  
  557. /* Start the main copy loop
  558.  */
  559. do_another:
  560.     if(!use_old_file)
  561.         {
  562.         if(time)
  563.             measure_speed(START_CLOCK, "");
  564.  
  565.         if(0 != (status = disk_rw(int13_drive, READ_TRK, 0, 1, (char *)bpb)))
  566.             {
  567.             ERROR_ALERT();
  568.             cprintf("\r\n  Error: unable to read floppy boot sector\r\n");
  569.             disk_error_print(status);
  570.             exit(1);
  571.             }
  572.         if( !Is_X && (512 != bpb->bytes_per_sector))
  573.             {
  574.             ERROR_ALERT();
  575.             cprintf("\r\n  Error: floppy boot sector indicates other than 512 "
  576.                       "byte sectors\r\n");
  577.             exit(1);
  578.             }
  579.         if(Is_X)
  580.             volname = "????";
  581.         else
  582.             volname = getvolid(dos_drive + 1);
  583.         }
  584.  
  585.     openmode = use_old_file ? "rb" : "w+b";
  586.     if(NULL == (fp = fopen(Tempfile, openmode)))
  587.         {
  588.         ERROR_ALERT();
  589.         cprintf("\r\n  Error: unable to open temporary file %s\r\n", Tempfile);
  590.         exit(1);
  591.         }
  592.     if(!(use_old_file || save_temp_file))
  593.         atexit(erase_temp_file);
  594.  
  595. /* Read floppy and copy to temporary file
  596.  */
  597.     if(use_old_file)
  598.         {
  599.         Hard_error = FALSE;
  600.         if(1 != fread(bpb, sizeof(struct BPB) + 12, 1, fp)
  601.             || 1 != fread(buffer, 512 - 12 - sizeof(struct BPB), 1, fp)
  602.             || Hard_error)
  603.             {
  604.             cprintf("\r\n  Error reading saved file %s\r\n", Tempfile);
  605.             exit(1);
  606.             }
  607.         volname = (char *)bpb + sizeof(struct BPB);
  608.         }
  609.  
  610.     if(Is_X)
  611.         {
  612.         Sd_per_dsk = 2 == X_type ? 2 : 1;
  613.         Tr_per_dsk = 80;
  614.         Sectors_per_track = 10;
  615.         Sectors_per_disk = Sd_per_dsk * Tr_per_dsk * Sectors_per_track;
  616.         }
  617.     else
  618.         {
  619.         Sectors_per_disk = bpb->total_sectors;
  620.         Sd_per_dsk = bpb->num_heads;
  621.         Tr_per_dsk = Sectors_per_disk / (Sd_per_dsk * bpb->sectors_per_track);
  622.         Sectors_per_track = bpb->sectors_per_track;
  623.         }
  624.  
  625. /* Select buffer size which is a multiple of the number of sectors per
  626.     track * number of sides.
  627.  */
  628.     max_sectors_use = (MAX_SECTORS/(Sectors_per_track*Sd_per_dsk))
  629.                                                                 *Sectors_per_track*Sd_per_dsk;
  630.     if(0 == max_sectors_use)
  631.         max_sectors_use = min(MAX_SECTORS, Sectors_per_track);
  632.  
  633.     if(!use_old_file)
  634.         {
  635.         first_sec = 0;
  636.         tot_sects = Sectors_per_disk;
  637.         cprintf("\r  Reading %d sectors, Volume %s, making %d copies...",
  638.                 tot_sects, volname, ncopies);
  639.         clreol();
  640.         statline(-tot_sects, 0);
  641.         memset(buffer, 0, 512);
  642.         Hard_error = FALSE;
  643.         if(1 != fwrite(bpb, sizeof(struct BPB), 1, fp)
  644.             || 1 != fwrite(volname, 12, 1, fp)
  645.             || 1 != fwrite(buffer, 512 - 12 - sizeof(struct BPB), 1, fp)
  646.             || Hard_error)
  647.             {
  648.             ERROR_ALERT();
  649.             cprintf("\r\n  Error: temp disk write failed\r\n");
  650.             exit(1);
  651.             }
  652.  
  653.         while(tot_sects > 0)
  654.             {
  655.             nread = min(max_sectors_use, tot_sects);
  656.             if(0 != (status = disk_rw(int13_drive, READ_TRK, first_sec, nread, (char *)buffer)))
  657.                 {
  658.                 ERROR_ALERT();
  659.                 cprintf("\r\n  Error: floppy read failed\r\n");
  660.                 disk_error_print(status);
  661.                 exit(1);
  662.                 }
  663. /* On first pass through here, check that the FAT indicates no bad sectors
  664.     on master disk.  (Can't do this for special formats since location of FAT
  665.     is unknown.)
  666.  */
  667.             if(0 == first_sec && !Is_X)
  668.                 {
  669.                 n_alloc = Sectors_per_disk / bpb->sectors_per_alloc;
  670.                 if(n_alloc < 4087)
  671.                     {
  672. /* 12-bit FAT
  673.  */
  674.                     fat_12 = (short *)(buffer + bpb->reserved_secs * 512 + 3);
  675.                     for(i=fat_sub=0; i<n_alloc; i++, fat_12 += 1 + fat_sub, fat_sub = 1 - fat_sub)
  676.                         {
  677.                         if((*fat_12 & fat12_mask[fat_sub]) != fat12_test[fat_sub])
  678.                             continue;
  679.                         ERROR_ALERT();
  680.                         cprintf("\r\n  Error: floppy has bad sectors marked in FAT"
  681.                                   "\r\n  WSScopyd only copies perfect disks\r\n");
  682. cprintf("\r\ni = %d, fat = %x\r\n", i, *fat_12);
  683.                         exit(1);
  684.                         }
  685.                      }
  686.                  else
  687.                     {
  688. /* 16-bit FAT (don't expect this code to ever be executed)
  689.  */
  690.                     ERROR_ALERT();
  691.                     cprintf("\r\n  Sorry, I don't know how to handle 16-bit "
  692.                               "FAT's\r\n");
  693.                     exit(1);
  694.                      }
  695.                  }
  696.             tot_sects -= nread;
  697.             first_sec += nread;
  698.             statline(tot_sects, READ_INDICATOR);
  699.             if(nread != fwrite(buffer, BYTES_PER_SECTOR, nread, fp))
  700.                 {
  701.                 ERROR_ALERT();
  702.                 cprintf("\r\n  Error: temp disk write failed\r\n");
  703.                 exit(1);
  704.                 }
  705.             }
  706.  
  707. /* Read first sector again to position drive head
  708.  */
  709.         disk_rw(int13_drive, READ_TRK, 0, 1, (char *)buffer);
  710.  
  711.         if(time)
  712.             {
  713.             measure_speed(READ_CLOCK, "Reading source disk");
  714.             cprintf("\n");
  715.             }
  716.         }
  717.  
  718. /* Done reading, ready to copy
  719.  */
  720.     for(icopy=0; icopy<ncopies; icopy++)
  721.         {
  722. wait_for_blank:
  723.         UP_ONE_AND_CLEAR();
  724.         cprintf("  Waiting for blank disk, hit a key...%c", beep);
  725. restart_after_error:
  726.         Hard_error = FALSE;
  727.         fseek(fp, 512, SEEK_SET);
  728.         key = getch();
  729.         if(3 == key || 0x1b == key)
  730.             {
  731.             fclose(fp);
  732.             cprintf("\r\n\n");
  733.             exit(0);
  734.             }
  735.  
  736.         if(time)
  737.             measure_speed(START_CLOCK, "");
  738.  
  739.         if(format)
  740.             {
  741.             tot_sects = Sectors_per_disk;
  742.             cprintf("\r  Formatting %d sectors, Volume %s, copy number "
  743.                     "%d of %d...", Sectors_per_disk, volname, icopy+1, ncopies);
  744.             clreol();
  745.             statline(-2*Tr_per_dsk, 0);
  746.  
  747.             if(ERR == disk_format(int13_drive, drive_type))
  748.                 {
  749.                 ERROR_ALERT();
  750.                 UP_ONE_AND_CLEAR();
  751.                 cprintf("\r\n  Error: floppy format failed, "
  752.                                                         "insert replacement disk...");
  753.                 goto restart_after_error;
  754.                 }
  755.  
  756.             if(time)
  757.                 {
  758.                 measure_speed(READ_CLOCK, "Formatting disk");
  759.                 cprintf("\n");
  760.                 measure_speed(START_CLOCK, "");
  761.                 }
  762.             UP_ONE_AND_CLEAR();
  763.             }
  764.         else if(!Is_X)
  765.             {
  766. /* Preformatted disk, read boot sector and make sure it is compatible.
  767.     Also check that there are no bad sectors marked in the FAT.  (Can't do
  768.     this for special formats since location of FAT is unknown.)
  769.  */
  770.             if(0 != (status = disk_rw(int13_drive, READ_TRK, 0,
  771.                                             1 + bpb->sectors_per_fat, (char *)buffer)))
  772.                 {
  773.                 ERROR_ALERT();
  774.                 cprintf("\r\n  Error: can't read boot sector, insert formatted "
  775.                                                                                 "disk...\r\n");
  776.                 disk_error_print(status);
  777.                 UP_N(3);
  778.                 goto restart_after_error;
  779.                 }
  780.             if(0 != memcmp(buffer+11, &bpb->bytes_per_sector, sizeof(struct BPB)-11))
  781.                 {
  782.                 ERROR_ALERT();
  783.                 cprintf("\r\n  Error: target disk is not the same format as "
  784.                                                     "master, insert replacement...\r\n\n");
  785.                 goto restart_after_error;
  786.                 }
  787.             n_alloc = Sectors_per_disk / bpb->sectors_per_alloc;
  788.             fat_12 = (short *)(buffer + bpb->reserved_secs * 512 + 3);
  789.             for(i=fat_sub=0; i<n_alloc; i++, fat_12 += 1 + fat_sub, fat_sub = 1 - fat_sub)
  790.                 {
  791.                 if((*fat_12 & fat12_mask[fat_sub]) != fat12_test[fat_sub])
  792.                     continue;
  793.                 ERROR_ALERT();
  794.                 cprintf("\r\n  Error: target floppy has bad sectors marked in "
  795.                                                         "FAT, insert replacement...\r\n\n");
  796.                 goto restart_after_error;
  797.                 }
  798.             }
  799.         tot_sects = Sectors_per_disk;
  800.         cprintf("\r  Writing %d sectors, Volume %s, copy number "
  801.                 "%d of %d...", tot_sects, volname, icopy+1, ncopies);
  802.         clreol();
  803.         first_sec = 0;
  804.         statline(-tot_sects, 0);
  805.         while(tot_sects > 0)
  806.             {
  807.             nwrite = min(max_sectors_use, tot_sects);
  808.             Hard_error = FALSE;
  809.             if(nwrite != fread(buffer, BYTES_PER_SECTOR, nwrite, fp)
  810.                 || Hard_error)
  811.                 {
  812.                 ERROR_ALERT();
  813.                 cprintf("\r\n  Error: temp disk read failed\r\n");
  814.                 exit(1);
  815.                 }
  816.  
  817.             if(0 != (status = disk_rw(int13_drive, WRITE_TRK, first_sec, nwrite,
  818.                                                                                 (char *)buffer)))
  819.                 {
  820.                 ERROR_ALERT();
  821.                 UP_ONE_AND_CLEAR();
  822.                 cprintf("\r\n  Error: floppy write failed, insert "
  823.                                                                     "replacement...\r\n");
  824.                 disk_error_print(status);
  825.                 UP_N(3);
  826.                 goto restart_after_error;
  827.                 }
  828.             tot_sects -= nwrite;
  829.             first_sec += nwrite;
  830.             statline(tot_sects, WRITE_INDICATOR);
  831.             }
  832.  
  833.         if(time)
  834.             {
  835.             measure_speed(READ_CLOCK, "Writing disk");
  836.             cprintf("\n");
  837.             }
  838.  
  839. /* Optionally verify by reading floppy and temp file and comparing
  840.  */
  841.         if(verify)
  842.             {
  843.             if(time)
  844.                 measure_speed(START_CLOCK, "");
  845.             fseek(fp, 512, SEEK_SET);
  846.             tot_sects = Sectors_per_disk;
  847.             cprintf("\r");        clreol();    UP_N(1);
  848.             cprintf("  Verifying %d sectors, Volume %s, "
  849.                     "copy number %d of %d...",
  850.                     tot_sects, volname, icopy+1, ncopies);
  851.             clreol();
  852.             first_sec = 0;
  853.             statline(-tot_sects, 0);
  854.             Hard_error = FALSE;
  855.             while(tot_sects > 0)
  856.                 {
  857.                 nver = min(max_sectors_use, tot_sects);
  858.                 Hard_error = FALSE;
  859.                 if(nver != fread(buffer, BYTES_PER_SECTOR, nver, fp)
  860.                     || Hard_error)
  861.                     {
  862.                     ERROR_ALERT();
  863.                     cprintf("\r\n  Error: temp disk read failed during verify\r\n");
  864.                     exit(1);
  865.                     }
  866.                 if(0 != (status = disk_rw(int13_drive, READ_TRK, first_sec, nver,
  867.                                                                                 (char *)buffer2)))
  868.                     {
  869.                     ERROR_ALERT();
  870.                     UP_ONE_AND_CLEAR();
  871.                     cprintf("\r\n  Error: floppy read failed during verify, "
  872.                                 "insert replacement...\r\n");
  873.                     disk_error_print(status);
  874.                     UP_N(3);
  875.                     goto restart_after_error;
  876.                     }
  877.                 nbytes = nver * BYTES_PER_SECTOR;
  878.                 cmp1 = (char *)buffer;
  879.                 cmp2 = (char *)buffer2;
  880.                 for(j=0; j<nbytes; j++)
  881.                     {
  882.                     if(*cmp1++ != *cmp2++)
  883.                         {
  884.                         ERROR_ALERT();
  885.                         UP_ONE_AND_CLEAR();
  886.                         cprintf("\r\n  Disk verify failed with unequal compare, "
  887.                                     "insert replacement...");
  888.                         UP_N(1);
  889.                         goto restart_after_error;
  890.                         }
  891.                     }
  892.                 tot_sects -= nver;
  893.                 first_sec += nver;
  894.                 statline(tot_sects, VERIFY_INDICATOR);
  895.                 }
  896.             if(time)
  897.                 {
  898.                 measure_speed(READ_CLOCK, "Verifying disk");
  899.                 cprintf("\n");
  900.                 }
  901.             }
  902.         }
  903.  
  904.     fclose(fp);
  905.     if(save_temp_file)
  906.         {
  907.         cprintf("\r\n  Disk image saved in file %s\r\n", Tempfile);
  908.         exit(0);
  909.         }
  910.     if(use_old_file)
  911.         {
  912.         cprintf("\r\n  %d copies completed\r\n", ncopies);
  913.         exit(0);
  914.         }
  915.     UP_ONE_AND_CLEAR();
  916.     cprintf("  Waiting for source disk, hit a key...%c", beep);
  917.     key = getch();
  918.     if(3 == key || 0x1b == key)
  919.         {
  920.         cprintf("\r\n\n");
  921.         exit(0);
  922.         }
  923.     goto do_another;
  924.     }
  925.  
  926. /*********************************************************************
  927.     statline - print initial or updated progress indicator
  928.     Initialize with sectors = -total number of sectors on disk (or whatever is
  929.     being counted).  Initialization call also clears the line below the
  930.     progress indicator.  On calls to update progress indicator, also check for
  931.     any keystrokes.  Exit on ^C or escape, flush anything else.
  932.  */
  933. void statline(int sectors, int color)
  934.     {
  935.     int count, i;
  936.     static int old_count, total_sectors;
  937.  
  938.     if(sectors < 0)
  939.         {
  940.         cprintf("\r\n\n");
  941.         clreol();
  942.         gotoxy(3, wherey()-1);
  943.         for(i=0; i<37; i++)
  944.             cprintf("\304\305");        /* special chars are -- and -|- */
  945.         cprintf("\304\264\r  ");        /* special chars are -- and -|  */
  946.         old_count = 0;
  947.         total_sectors = -sectors;
  948.         }
  949.     else
  950.         {
  951.         count = (int)((76L*(total_sectors - sectors))/total_sectors);
  952.         textcolor(color);
  953.         for(i=old_count; i<count; i++)
  954.             cprintf("\333");            /* special char is solid block */
  955.         textcolor(TEXT_COLOR);
  956.         old_count = count;
  957.         while(kbhit())
  958.             {
  959.             switch(getch())
  960.                 {
  961.                 case 3:            /* control-c */
  962.                 case 0x1b:        /* escape */
  963.                     cprintf("\r\n  Aborting\r\n");
  964.                     exit(1);
  965.  
  966.                 default:
  967.                     continue;
  968.                 }
  969.             }
  970.         }
  971.     }
  972.  
  973. /*********************************************************************
  974.     measure_speed - measure how much time elapsed between successive calls
  975.     Start timing by calling with function=0, end timing with function=1
  976.     and thing_timed a descriptive string.  After a type 1 call, the start
  977.     time is preserved so that cummulative timing can be done.
  978.  */
  979. #include <sys/timeb.h>
  980. void measure_speed(int function, char *thing_timed)
  981.     {
  982.     static struct timeb start;
  983.     struct timeb finish;
  984.     long seconds;
  985.     short ms;
  986.  
  987.     switch(function)
  988.         {
  989.         case START_CLOCK:
  990.             ftime(&start);
  991.             break;
  992.  
  993.         case READ_CLOCK:
  994.             ftime(&finish);
  995.             seconds=finish.time-start.time;
  996.             ms=finish.millitm-start.millitm;
  997.             if(ms < 0)
  998.                 {
  999.                 seconds--;
  1000.                 ms += 1000;
  1001.                 }
  1002.             cprintf("\r  %s took %ld.%03hd seconds", thing_timed, seconds, ms);
  1003.             clreol();    cprintf("\r\n");
  1004.             break;
  1005.         }
  1006.     return;
  1007.     }
  1008.  
  1009. /*********************************************************************
  1010.     getvolid - get volume label from disk to be copied, return in static
  1011.     variable.  Arg is drive number (0=A,1=B,...)
  1012.  */
  1013. struct dos_fcb {
  1014.     unsigned char flag;        /* ff to indicate extended fcb */
  1015.     char res[5];                /* reserved */
  1016.     unsigned char vattr;        /* attribute */
  1017.     unsigned char drive;        /* drive, 1=A, 2=B,... */
  1018.     unsigned char vn[11];    /* file or volume name */
  1019.     unsigned char fattr;        /* attributes of found file */
  1020.     char resrvd[10];            /* reserved */
  1021.     unsigned int time;        /* last write time */
  1022.     unsigned int date;        /* last write date */
  1023.     long size;                    /* size of file */
  1024.     };
  1025. char *getvolid(int dos_drive)
  1026.     {
  1027.     struct dos_fcb fcb1, fcb2;
  1028.     static char volid[12];
  1029.     struct SREGS sregs;
  1030.     union REGS regs;
  1031.  
  1032.     setdta((char far *)&fcb1);
  1033.     memset(&fcb2, 0, sizeof(struct dos_fcb));
  1034.     sregs.ds = FP_SEG(&fcb2);
  1035.     fcb2.flag = 0xff;
  1036.     fcb2.vattr = 8;
  1037.     fcb2.drive = dos_drive;
  1038.     strncpy((char *)fcb2.vn, "???????????", 11);
  1039.     regs.x.dx = FP_OFF(&fcb2);
  1040.     regs.h.ah = 0x11;                /* search for first entry */
  1041.     intdosx(®s, ®s, &sregs);
  1042.     if(regs.h.al)
  1043.         strcpy(volid, "<none>");
  1044.     else
  1045.         strncpy(volid, (char *)fcb1.vn, 11);
  1046.     volid[11] = '\0';
  1047.     return(volid);
  1048.     }
  1049.  
  1050. /*********************************************************************
  1051.     Routine disk_format is derived from ZFMAT.C by Edward V. Dong.
  1052.     That code did not work for 360K in 1.2M under DOS 5, nor did it handle
  1053.     1.2M or 3.5" disks.
  1054.  */
  1055.  
  1056. /* ----------------------------------------------------------------
  1057.  
  1058.     Copyright 1988 by Edward V.  Dong, All Rights Reserved.
  1059.  
  1060.     This source code is placed into the public domain.  However,
  1061.     contributions are always welcome.  This is a modified form of the
  1062.     source code used in the author's ZIP program.
  1063.  
  1064.     Edward V Dong's Floppy Formattor is a Turbo C program to format
  1065.     floppy diskettes, based on earlier work by Frank Nystrom
  1066.    (COPYIT.C;CIS 71631,355), Kim Kokkonen (FMAT.PAS; Compuserve
  1067.    72457,2131), and Peter Norton (BOOT.ASM).  This programs formats,
  1068.    accurately, 360K diskettes in either standard 360K drives or 1.2M
  1069.     drives.
  1070.  
  1071.     To format 360K diskettes in 1.2 megabyte drives, the DASD must be
  1072.     set - this is only possible for DOS 3.0 or higher and for AT class
  1073.     machines.  The routines here are based on Kokkonen's Turbo Pascal
  1074.     work, ported to Turbo C.
  1075.  
  1076.     Edward V. Dong, 12205 Alexandria Pl, Chino, CA 91710.   18 May 1988
  1077.  
  1078.   ----------------------------------------------------------------*/
  1079.  
  1080. /*  Disk Constants...*/
  1081.  
  1082. #define MAX_SECTORS_PER_TRACK    18
  1083.  
  1084. #define FORMAT_360K 1
  1085. #define FORMAT_12MB 2
  1086. #define FORMAT_720K 3
  1087. #define FORMAT_144M 4
  1088.  
  1089. #define FORMAT_X      99
  1090.  
  1091. /* When you initialize the disk you need to pass the bios some things.
  1092.  */
  1093.  
  1094. struct
  1095.     {
  1096.     char    cyl,
  1097.             head,
  1098.             rec,
  1099.             num;
  1100.     } format_record[MAX_SECTORS_PER_TRACK];
  1101.  
  1102. /*********************************************************************
  1103.     disk_format - this does the formatting
  1104.  */
  1105. int disk_format(int int13_drive, int drive_type)
  1106.     {
  1107.     register int tr, sd;
  1108.     int  s, tries, status, success;
  1109.     int format_type, master_type;
  1110.     LOGICAL call_17h;
  1111.  
  1112. /*    Get and save address of old disk parameter block, copy to a replacement
  1113.     block (fields will be changed later).  Restore the old block when we
  1114.     terminate.
  1115.  */
  1116.     System_dpb = getvect(0x1e);
  1117.     movedata(FP_SEG(System_dpb), FP_OFF(System_dpb),
  1118.                 FP_SEG(&my_dpb), FP_OFF(&my_dpb), sizeof(struct DPB));
  1119.     setvect(0x1e, (void interrupt (*)()) &my_dpb);
  1120.     atexit(reset_disk_controller);
  1121.  
  1122.     status = ERR;
  1123.  
  1124.     if(Is_X)
  1125.         master_type = FORMAT_X;
  1126.     else if(Tr_per_dsk < 80)
  1127.         master_type = FORMAT_360K;
  1128.     else if(80 == Tr_per_dsk && 15 == Sectors_per_track)
  1129.         master_type = FORMAT_12MB;
  1130.     else if(9 == Sectors_per_track)
  1131.         master_type = FORMAT_720K;
  1132.     else if(18 == Sectors_per_track)
  1133.         master_type = FORMAT_144M;
  1134.     else
  1135.         {
  1136.         cprintf("\r\n  Unrecognized disk type, can't format\r\n");
  1137.         goto exit_format;
  1138.         }
  1139.  
  1140.     my_dpb.spt = Sectors_per_track;
  1141.     call_17h = TRUE;
  1142.     switch(drive_type)
  1143.         {
  1144.         case FLOPPY_360K:
  1145.             my_dpb.gap = 0x2a;        /* 5.25" disk */
  1146.             my_dpb.gap_l = 0x50;
  1147.             switch(master_type)
  1148.                 {
  1149.                 case FORMAT_360K:
  1150.                     format_type = 1;
  1151.                     goto set_format_type;
  1152.  
  1153.                 case FORMAT_12MB:
  1154.                 case FORMAT_720K:
  1155.                 case FORMAT_144M:
  1156.                 case FORMAT_X:
  1157.                     goto mismatch;
  1158.                 }
  1159.  
  1160.         case FLOPPY_12MB:
  1161.             my_dpb.gap = 0x2a;        /* 5.25" disk */
  1162.             my_dpb.gap_l = 0x50;
  1163.             switch(master_type)
  1164.                 {
  1165.                 case FORMAT_360K:
  1166.                 case FORMAT_X:
  1167.                     format_type = 2;
  1168.                     goto set_format_type;
  1169.  
  1170.                 case FORMAT_12MB:
  1171.                     format_type = 3;
  1172.                     goto set_format_type;
  1173.  
  1174.                 case FORMAT_720K:
  1175.                 case FORMAT_144M:
  1176.                     goto mismatch;
  1177.                 }
  1178.         case FLOPPY_720K:
  1179.             my_dpb.gap = 0x1b;        /* 3.5" disk */
  1180.             my_dpb.gap_l = 0x6c;
  1181.             switch(master_type)
  1182.                 {
  1183.                 case FORMAT_360K:
  1184.                 case FORMAT_12MB:
  1185.                 case FORMAT_144M:
  1186.                 case FORMAT_X:
  1187.                     goto mismatch;
  1188.  
  1189.                 case FORMAT_720K:
  1190.                     format_type = 4;
  1191.                     goto set_format_type;
  1192.                 }
  1193.         case FLOPPY_144M:
  1194.             my_dpb.gap = 0x1b;        /* 3.5" disk */
  1195.             my_dpb.gap_l = 0x6c;
  1196.             call_17h = FALSE;
  1197.             switch(master_type)
  1198.                 {
  1199.                 case FORMAT_360K:
  1200.                 case FORMAT_12MB:
  1201.                 case FORMAT_X:
  1202.                     goto mismatch;
  1203.  
  1204.                 case FORMAT_144M:
  1205.                 case FORMAT_720K:
  1206.                     goto set_format_type;
  1207.                 }
  1208.         }
  1209.  
  1210. mismatch:
  1211.     cprintf("  \r\nDrive not capable of requested disk format\r\n\n");
  1212.     goto exit_format;
  1213.  
  1214. set_format_type:
  1215.     if(0 != (0xff00 & biosdisk(0x18, int13_drive, 0, Tr_per_dsk,
  1216.                                         Sectors_per_track, 0, format_record)))
  1217.         {
  1218.         cprintf("\r\n  biosdisk call with ah=18h failed\r\n\n");
  1219.         goto exit_format;
  1220.         }
  1221.  
  1222.     if(call_17h)
  1223.         {
  1224.         biosdisk(0x17, int13_drive, 0, 0, 0, format_type, format_record);
  1225.         if(0 != biosdisk(0x17, int13_drive, 0, 0, 0, format_type, format_record))
  1226.             {
  1227.             cprintf("\r\n  biosdisk call with ah=17h failed\r\n\n");
  1228.             goto exit_format;
  1229.             }
  1230.         }
  1231.  
  1232.     if(Is_X && (int13_drive < 4))
  1233.         {
  1234.         poke(0x40, 0x90 + int13_drive, 0x54);
  1235.         my_dpb.gap = 0x1b;        /* copied from 800kfmat.asm */
  1236.         my_dpb.gap_l = 0x28;
  1237.         }
  1238.  
  1239. /* Use BIOS to format tracks
  1240.  */
  1241.     for(tr = 0;   tr < Tr_per_dsk;  tr++)
  1242.         {
  1243.         for(sd = 0;  sd < Sd_per_dsk;  sd++)
  1244.             {
  1245.             for(s = 0;  s < Sectors_per_track;  s++)
  1246.                 {
  1247.                 format_record[s].cyl = tr;
  1248.                 format_record[s].head = sd;
  1249.                 format_record[s].rec = s + 1;
  1250.                 format_record[s].num = 2;
  1251.                 }
  1252. /* RX-50 disks have 2:1 interleave except for first two tracks
  1253.  */
  1254.             if(Is_X && 3 == X_type && tr > 1)
  1255.                 {
  1256.                 for(s = 0;  s < Sectors_per_track;  s+=2)
  1257.                     {
  1258.                     format_record[s].rec = s/2 + 1;
  1259.                     format_record[s+1].rec = s/2 + 6;
  1260.                     }
  1261.                 }
  1262.             success = tries = 0;
  1263.             while (!success && (tries < 3))
  1264.                 {
  1265.                 if(0 == biosdisk(FORMAT_TRK, int13_drive, sd, tr, 1,
  1266.                                       Sectors_per_track, &format_record[0]))
  1267.                     success = 1;
  1268.                 else
  1269.                     {
  1270.                     biosdisk(RESET_CONTROLLER, 0, 0, 0, 0, 0, (void *)0);
  1271.                     tries++;
  1272.                     }
  1273.                 }
  1274.             if (success)
  1275.                 statline(2*(Tr_per_dsk - tr) - sd, FORMAT_INDICATOR);
  1276.             else
  1277.                 {
  1278.                 cprintf("\r\n  Format Error - aborting...\r\n");
  1279.                 exit(1);
  1280.                 }
  1281.             }
  1282.         }
  1283.     status = NO_ERR;
  1284. exit_format:
  1285. /* Restore interrupt vector 0x1e.  Perhaps doing this is unnecessary, but it
  1286.     seems best to leave things as we found them.
  1287.  */
  1288.     reset_disk_controller();
  1289.     return(status);
  1290.     }
  1291.  
  1292. /*********************************************************************
  1293.     erase_temp_file - called when program terminates if the disk file used to
  1294.     hold the floppy image is to be erased.
  1295.  */
  1296. void erase_temp_file(void)
  1297.     {
  1298.     remove(Tempfile);
  1299.     return;
  1300.     }
  1301.  
  1302. /*********************************************************************
  1303.     disk_rw - read or write floppy using int 13h
  1304.     This routine uses 0-based sector numbering in calculations, then adjusts
  1305.     for 1-based sectors in the biosdisk call.
  1306.  */
  1307.  
  1308. LOGICAL disk_rw(int int13_drive, int action, int first_sector, int nsects,
  1309.                      char *buffer)
  1310.     {
  1311.     int hs, n_rw, retries, sector, side, status, track;
  1312.  
  1313.     if(Is_X && (0 == first_sector) && (int13_drive < 4))
  1314.         poke(0x40, 0x90 + int13_drive, 0x54);
  1315.  
  1316.     hs = Sd_per_dsk * Sectors_per_track;
  1317.     track = first_sector / hs;
  1318.     side = Sd_per_dsk > 1 ? (first_sector/Sectors_per_track) & 1 : 0;
  1319.     sector = first_sector - track*hs - side*Sectors_per_track;
  1320.     while(nsects > 0)
  1321.         {
  1322.         n_rw = Sectors_per_track - sector;
  1323.         n_rw = min(n_rw, nsects);
  1324.         retries = 0;
  1325.         do {
  1326.             status = biosdisk(action, int13_drive, side, track, sector+1, n_rw,
  1327.                                     buffer);
  1328.             }
  1329.         while (0 != status && ++retries < 3);
  1330.         if(0 != status)
  1331.             return(status);
  1332.         nsects -= n_rw;
  1333.         sector = 0;
  1334.         if(++side >= Sd_per_dsk)
  1335.             {
  1336.             side = 0;
  1337.             track++;
  1338.             }
  1339.         buffer += BYTES_PER_SECTOR * n_rw;
  1340.         }
  1341.     return(NO_ERR);
  1342.     }
  1343.  
  1344. /* Called when we exit to reset things.  If controller is not reset, some
  1345.     errors may leave floppy controller in a state where nothing works.
  1346.  */
  1347. void reset_disk_controller(void)
  1348.     {
  1349.     setvect(0x1e, System_dpb);
  1350.     biosdisk(RESET_CONTROLLER, 0, 0, 0, 0, 0, (void *)0);
  1351.     return;
  1352.     }
  1353.  
  1354. void disk_error_print(unsigned int error_code)
  1355.     {
  1356.     char *disk_errors[18]={"success", "invalid function",
  1357.         "address mark not found", "disk write-protected", "sector not found",
  1358.         "reset failed", "disk changed", "activity failed", "DMA overrun",
  1359.         "DMA across 64K boundary", "bad sector detected", "bad track detected",
  1360.         "unsupported track or invalid media",
  1361.         "invalid number of sectors on format",
  1362.         "control data address mark detected",
  1363.         "DMA arbitration level out of range",
  1364.         "uncorrectable CRC or ECC error on read", "data ECC corrected"};
  1365.  
  1366.     cprintf("  Disk error is ");
  1367.     if(0x20 == error_code)
  1368.         cprintf("controller failure");
  1369.     else if(0x40 == error_code)
  1370.         cprintf("seek failed");
  1371.     else if(0x80 == error_code)
  1372.         cprintf("timeout (not ready)");
  1373.     else if(error_code < 18)
  1374.         cprintf(disk_errors[error_code]);
  1375.     else cprintf("unknown error, code=%x", error_code);
  1376.     cprintf("\r\n");
  1377.     return;
  1378.     }
  1379.